﻿#include "as_socket.h"
#ifdef _WIN32
#pragma comment(lib, "Ws2_32.lib")
#else
#include <fcntl.h>
#endif
#include <iostream>
#include "log.h"
#include "cross_comm.h"

as::net::Socket::Socket(socket_fd fd /*= 0*/)
{
	fd_ = fd;
}

socket_fd as::net::Socket::create_socket(int af)
{
	socket_fd fd = ::socket(af, SOCK_STREAM, IPPROTO_TCP);
	return fd;
}

int as::net::Socket::bind(const Address &addr)
{
	int ret = ::bind(fd_, addr.GetSockAddr(), addr.GetLen());
	return ret;
}

int as::net::Socket::connect(const Address &addr)
{
	int ret = ::connect(fd_, addr.GetSockAddr(), addr.GetLen());
	return ret;
}

int as::net::Socket::listen()
{
	int ret = ::listen(fd_, SOMAXCONN);
	return ret;
}

void as::net::Socket::close()
{
	close(fd_);
}

void as::net::Socket::shutdown(int how)
{
	shutdown(fd_, how);
}

void as::net::Socket::shutdown(socket_fd fd, int how)
{
	::shutdown(fd, how);
}

void as::net::Socket::close(socket_fd fd)
{
#ifdef _WIN32
	closesocket(fd);
#else
	::close(fd);
#endif
}

void as::net::Socket::set_non_block()
{
	set_non_block(fd_);
}

void as::net::Socket::set_non_block(socket_fd fd)
{
#ifdef _WIN32    
	unsigned long set = 1;
	ioctlsocket(fd, FIONBIO, &set);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#endif
}

void as::net::Socket::set_keep_alive()
{
	set_keep_alive(fd_);
}

void as::net::Socket::set_recv_buff(int size)
{
	set_recv_buff(fd_, size);
}

void as::net::Socket::set_recv_buff(socket_fd fd, int size)
{
#ifdef _WIN32
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&size, sizeof(size));
#else
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
#endif
}

void as::net::Socket::set_send_buff(int size)
{
	set_send_buff(fd_, size);
}

void as::net::Socket::set_send_buff(socket_fd fd, int size)
{
#ifdef _WIN32
	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&size, sizeof(size));
#else
	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
#endif
}
void as::net::Socket::set_keep_alive(socket_fd fd)
{
#ifdef _WIN32
	//tcp_keepalive alive_in;
	//tcp_keepalive alive_out;
	//alive_in.onoff = TRUE;
	//alive_in.keepalivetime = 1000 * 60;
	//alive_in.keepaliveinterval = 1000 * 10;
	//unsigned long ulBytesReturn = 0;
	//if (SOCKET_ERROR == WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL))
	//{
	//	std::cerr << "set keep alive error:" << WSAGetLastError() << std::endl;
	//}
#else
	int opt = 1;
	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
#endif
}

socket_fd as::net::Socket::accept(const Address &peekAddr)
{
	sockaddr *addr = peekAddr.GetSockAddr();
#ifdef WIN32
	int addrLen = peekAddr.GetLen();
#else
	socklen_t addrLen;
#endif
	return as::net::Socket::accept(fd_, addr, &addrLen);
}

socket_fd as::net::Socket::accept(socket_fd fd, sockaddr *addr, socket_len len)
{
	return ::accept(fd, addr, len);
}

int as::net::Socket::write(const void *buf, int size)
{
	return write(fd_, buf, size);
}

int as::net::Socket::write(socket_fd fd, const void *buf, int size)
{
#ifdef _WIN32	
	return ::send(fd, (const char*)buf, size, 0);
#else
	return ::write(fd, (const char*)buf, size);
#endif
}


int as::net::Socket::recv(socket_fd fd, void *buf, int size)
{
#ifdef _WIN32
	return ::recv(fd, (char*)buf, size, 0);
#else
	return ::read(fd, buf, size);
#endif
}

int as::net::Socket::recv(void *buf, int size)
{
	return recv(fd_, buf, size);
}

int as::net::Socket::get_last_err(socket_fd fd)
{
	int optval;
	socklen_t optlen = static_cast<socklen_t>(sizeof optval);

	if (::getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen) < 0)
	{
		return errno;
	}
	else
	{
		return optval;
	}
}

int as::net::Socket::get_last_err()
{
	return get_last_err(fd_);
}

as::net::Address as::net::Socket::get_local_addr(socket_fd fd)
{
	Address addr;
	socklen_t addrlen = static_cast<socklen_t>(sizeof addr.addr6_);
	memset(&addr.addr6_, 0, addrlen);
	if (::getsockname(fd, (sockaddr*)(&addr.addr6_), &addrlen) < 0)
	{
		LOG_ERROR << "local addr error:" << fd << " error:" << as::tools::GetLastError();
	}
	return addr;
}

as::net::Address as::net::Socket::get_peer_addr(socket_fd sockfd)
{
	Address addr;
	socklen_t addrlen = static_cast<socklen_t>(sizeof addr.addr6_);
	memset(&addr.addr6_, 0, addrlen);
	if (::getpeername(sockfd, (sockaddr*)(&addr.addr6_), &addrlen) < 0) 
	{
		LOG_ERROR << "peer addr error:" << sockfd << " error:" << as::tools::GetLastError();
	}
	return addr;
}

void as::net::Socket::set_fd(socket_fd fd)
{
	fd_ = fd;
}
